#include <Arduino.h>
#include <WiFi.h>
#include <string>
#include <vector>
#include <WebServer.h>

#define PIN_SHCP_DIGITAL_ONE 5
#define PIN_STCP_DIGITAL_ONE 8
#define PIN_DS_DIGITAL_ONE   4

#define PIN_SHCP_DIGITAL_TWO  6
#define PIN_STCP_DIGITAL_TWO  7
#define PIN_DS_DIGITAL_TWO   12

#define PIN_SHCP_DIGITAL_THREE 0
#define PIN_STCP_DIGITAL_THREE 19
#define PIN_DS_DIGITAL_THREE  13

#define PIN_SHCP_DIGITAL_FOUR 2 
#define PIN_STCP_DIGITAL_FOUR 3
#define PIN_DS_DIGITAL_FOUR  10


#define DIGITAL_ONE  1
#define DIGITAL_TWO  2
#define DIGITAL_THREE  3
#define DIGITAL_FOUR  4

const int duration = 80;
const int interval = 100;

uint8_t shiftData[4];
String qnums[4];

// 模式枚举
enum DisplayMode {
  MODE_TIME,
  MODE_SCORE
};

DisplayMode currentMode = MODE_TIME; // 默认时间模式
int score1 = 0; // 比分1
int score2 = 0; // 比分2
int inputHours = 0; // 输入的小时
int inputMinutes = 0; // 输入的分钟
unsigned long lastMinuteUpdate = 0; // 上次分钟更新时间

// WiFiUDP ntpUDP;
// NTPClient timeClient(ntpUDP, "pool.ntp.org", 8 * 3600); // 东八区时间
WebServer server(80);

void encodeShiftRegister(String qnums[4], uint8_t* outBits);
void setBit(uint8_t* bits, int bitIndex, uint8_t val);
void writeShiftRegisterOne(uint8_t* data, int len);
void writeShiftRegisterTwo(uint8_t* data, int len);
void writeShiftRegisterThree(uint8_t* data, int len);
void writeShiftRegisterFour(uint8_t* data, int len);

void convertToRegisters(String s1, String s2, String s3, String s4, String s5, String output[4]);
void displayOnOneAndTwo(int num);
void displayOnThreeAndFour(int num);

// 函数指针
void (*writeRigister[4])(uint8_t*, int) = {writeShiftRegisterOne, writeShiftRegisterTwo, 
  writeShiftRegisterThree, writeShiftRegisterFour};  // 函数指针数组

// 翻转flip函数，输入qnums【】数组，输出翻转的动作
void flip(int _index, String string1, String string2, String string3, String string4, String string5, int _duration);

void noFlip(int _interval){
  flip(1, "000", "000", "000", "000", "000", _interval);
  flip(2, "000", "000", "000", "000", "000", _interval);
  flip(3, "000", "000", "000", "000", "000", _interval);
  flip(4, "000", "000", "000", "000", "000", _interval);
  digitalWrite(PIN_STCP_DIGITAL_ONE, HIGH);
  digitalWrite(PIN_STCP_DIGITAL_TWO, HIGH);
  digitalWrite(PIN_STCP_DIGITAL_THREE, HIGH);
  digitalWrite(PIN_STCP_DIGITAL_FOUR, HIGH);

  delay(_interval);
}


// wifi设置函数
void setup_wifi() {
  WiFi.useStaticBuffers(true);
  WiFi.mode(WIFI_STA);
  Serial.println("Connecting to Wi-Fi.");
  WiFi.begin(bssid, bpass, 6);
  WiFi.setTxPower(WIFI_POWER_8_5dBm);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.print(".");
  }
  Serial.print("\n Wi-Fi connected. IP:");
  Serial.println(WiFi.localIP());
}

// Web页面HTML
const char* htmlPage = R"rawliteral(
<!DOCTYPE HTML>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <title>ESP32 数字显示控制</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <style>
    body { font-family: Arial; text-align: center; margin: 0 auto; padding: 20px; }
    .container { display: flex; flex-direction: column; max-width: 400px; margin: 0 auto; }
    .mode-btn { margin: 10px; padding: 10px; font-size: 16px; }
    .form-group { margin: 15px 0; text-align: left; }
    label { display: block; margin-bottom: 5px; }
    input { width: 100%; padding: 8px; box-sizing: border-box; }
    button { background-color: #4CAF50; color: white; border: none; padding: 10px; cursor: pointer; }
    button:hover { background-color: #45a049; }
    #status-message { color: green; margin: 15px 0; height: 20px; }
  </style>
</head>
<body>
  <div class="container">
    <h1>数字显示控制</h1>
    <div id="status-message"></div>
    
    <button class="mode-btn" onclick="showForm('time')">时间模式</button>
    <div id="time-form" style="display: none;">
      <div class="form-group">
        <label for="hours">小时 (0-23):</label>
        <input type="number" id="hours" min="0" max="23" required>
      </div>
      <div class="form-group">
        <label for="minutes">分钟 (0-59):</label>
        <input type="number" id="minutes" min="0" max="59" required>
      </div>
      <button onclick="submitTime()">设置时间</button>
    </div>
    
    <button class="mode-btn" onclick="showForm('score')">比分模式</button>
    <div id="score-form" style="display: none;">
      <div class="form-group">
        <label for="score1">比分1 (0-99):</label>
        <input type="number" id="score1" min="0" max="99" required>
      </div>
      <div class="form-group">
        <label for="score2">比分2 (0-99):</label>
        <input type="number" id="score2" min="0" max="99" required>
      </div>
      <button onclick="submitScore()">设置比分</button>
    </div>
  </div>
  
  <script>
    function showForm(mode) {
      document.getElementById('time-form').style.display = 'none';
      document.getElementById('score-form').style.display = 'none';
      
      if (mode === 'time') {
        document.getElementById('time-form').style.display = 'block';
      } else if (mode === 'score') {
        document.getElementById('score-form').style.display = 'block';
      }
    }

    function submitTime() {
      const hours = document.getElementById('hours').value;
      const minutes = document.getElementById('minutes').value;
      fetch(`/settime?hours=${hours}&minutes=${minutes}`)
        .then(response => response.text())
        .then(data => {
          document.getElementById('status-message').textContent = data;
          setTimeout(() => document.getElementById('status-message').textContent = "", 3000);
        });
    }

    function submitScore() {
      const score1 = document.getElementById('score1').value;
      const score2 = document.getElementById('score2').value;
      fetch(`/setscore?score1=${score1}&score2=${score2}`)
        .then(response => response.text())
        .then(data => {
          document.getElementById('status-message').textContent = data;
          setTimeout(() => document.getElementById('status-message').textContent = "", 3000);
        });
    }
  </script>
</body>
</html>
)rawliteral";


// 处理根路径请求
void handleRoot() {
  server.sendHeader("Content-Type", "text/html; charset=utf-8");
  server.send(200, "text/html", htmlPage);
}

// 设置时间
void handleSetTime() {
  if (server.hasArg("hours") && server.hasArg("minutes")) {
    inputHours = server.arg("hours").toInt();
    inputMinutes = server.arg("minutes").toInt();
    currentMode = MODE_TIME;
    lastMinuteUpdate = millis(); // 重置计时
    
    // 立即更新时间显示
    displayOnOneAndTwo(inputHours);
    noFlip(100);
    displayOnThreeAndFour(inputMinutes);
    noFlip(100);
    
    server.send(200, "text/plain", "时间已设置为: " + String(inputHours) + ":" + String(inputMinutes));
  } else {
    server.send(400, "text/plain", "缺少参数");
  }
}

// 设置比分
void handleSetScore() {
  if (server.hasArg("score1") && server.hasArg("score2")) {
    score1 = server.arg("score1").toInt();
    score2 = server.arg("score2").toInt();
    currentMode = MODE_SCORE;
    
    // 立即更新比分显示
    displayOnOneAndTwo(score1);
    noFlip(100);
    displayOnThreeAndFour(score2);
    noFlip(100);
    
    server.send(200, "text/plain", "比分已设置为: " + String(score1) + " - " + String(score2));
  } else {
    server.send(400, "text/plain", "缺少参数");
  }
}

void setup() {
  Serial.begin(115200, SERIAL_8N1, 20, 21);

  delay(1000);
  setup_wifi();  //初始化wifi
  
  // 初始化引脚
  pinMode(PIN_SHCP_DIGITAL_ONE, OUTPUT);
  pinMode(PIN_STCP_DIGITAL_ONE, OUTPUT);
  pinMode(PIN_DS_DIGITAL_ONE, OUTPUT);
  pinMode(PIN_SHCP_DIGITAL_TWO, OUTPUT);
  pinMode(PIN_STCP_DIGITAL_TWO, OUTPUT);
  pinMode(PIN_DS_DIGITAL_TWO, OUTPUT);
  pinMode(PIN_SHCP_DIGITAL_THREE, OUTPUT);
  pinMode(PIN_STCP_DIGITAL_THREE, OUTPUT);
  pinMode(PIN_DS_DIGITAL_THREE, OUTPUT);
  pinMode(PIN_SHCP_DIGITAL_FOUR, OUTPUT);
  pinMode(PIN_STCP_DIGITAL_FOUR, OUTPUT);
  pinMode(PIN_DS_DIGITAL_FOUR, OUTPUT);

  // 初始化NTP客户端
  // timeClient.begin();
  // timeClient.update();
  
  // 设置Web服务器路由
  server.on("/", handleRoot);
  server.on("/settime", handleSetTime);
  server.on("/setscore", handleSetScore);
  
  server.begin();
  Serial.println("HTTP server started");
  
  // 初始显示
  displayOnOneAndTwo(0);
  noFlip(100);
  displayOnThreeAndFour(0);
  noFlip(100);  
}

void loop() {
  server.handleClient();
  if (currentMode == MODE_TIME) {
    // 每分钟更新一次时间（完全依赖内部计时）
    unsigned long currentMillis = millis();
    if (currentMillis - lastMinuteUpdate >= 60000) {
      inputMinutes++;
      if (inputMinutes >= 60) {
        inputMinutes = 0;
        inputHours++;
        if (inputHours >= 24) {
          inputHours = 0;
        }
      }
      lastMinuteUpdate = currentMillis;
      
      // 更新时间显示
      displayOnOneAndTwo(inputHours);
      noFlip(100);
      displayOnThreeAndFour(inputMinutes);
      noFlip(interval);
    }
  }
  else if (currentMode == MODE_SCORE) {
    // 比分模式不需要自动更新，保持显示即可
    // 可以在这里添加其他比分模式下的逻辑
  }
  
  delay(1000); // 防止过于频繁的循环
  // displayOnOneAndTwo(88);
  // noFlip(100);
  // displayOnThreeAndFour(88);
  // noFlip(2000);

  // displayOnOneAndTwo(-1);
  // noFlip(100);
  // displayOnThreeAndFour(-1);
  // noFlip(2000);

}

// 把显示字符数组转换成二进制代码，outBits为输出二进制字节指针
void encodeShiftRegister(String qnums[4], uint8_t* outBits) {
  memset(outBits, 0, 4);
  int bitIndex = 0;
  for (int i = 0; i < 4; i++) {
    for (int j = 0; j < 4; j++) {
      char digit = qnums[i][j];
      // uint8_t b1 = 0, b2 = 0;
      uint8_t b1 = 0, b2 = 0;
      if (digit == '1') { b1 = 0; b2 = 1; }
      else if (digit == '2') { b1 = 1; b2 = 0; }

      setBit(outBits, bitIndex++, b1);
      setBit(outBits, bitIndex++, b2);
    }
  }
}

// 转换
void setBit(uint8_t* bits, int bitIndex, uint8_t val) {
  int byteIndex = bitIndex / 8;
  int bitOffset = 7 - (bitIndex % 8);
  if (val)
    bits[byteIndex] |= (1 << bitOffset);
  else
    bits[byteIndex] &= ~(1 << bitOffset);
}

// 写入第一个数字的寄存器
void writeShiftRegisterOne(uint8_t* data, int len) {
  digitalWrite(PIN_STCP_DIGITAL_ONE, LOW);
  for (int i = 0; i < len; i++) {
    shiftOut(PIN_DS_DIGITAL_ONE, PIN_SHCP_DIGITAL_ONE, LSBFIRST, data[i]);
  }
  // digitalWrite(PIN_STCP_DIGITAL_ONE, HIGH);
}

// 写入第二个数字的寄存器
void writeShiftRegisterTwo(uint8_t* data, int len) {
  digitalWrite(PIN_STCP_DIGITAL_TWO, LOW);
  for (int i = 0; i < len; i++) {
    shiftOut(PIN_DS_DIGITAL_TWO, PIN_SHCP_DIGITAL_TWO, LSBFIRST, data[i]);
  }
  // digitalWrite(PIN_STCP_DIGITAL_TWO, HIGH);
}

// 写入第三个数字的寄存器
void writeShiftRegisterThree(uint8_t* data, int len) {
  digitalWrite(PIN_STCP_DIGITAL_THREE, LOW);
  for (int i = 0; i < len; i++) {
    shiftOut(PIN_DS_DIGITAL_THREE, PIN_SHCP_DIGITAL_THREE, LSBFIRST, data[i]);
  }
  // digitalWrite(PIN_STCP_DIGITAL_THREE, HIGH);
}

// 写入第四个数字的寄存器
void writeShiftRegisterFour(uint8_t* data, int len) {
  digitalWrite(PIN_STCP_DIGITAL_FOUR, LOW);
  for (int i = 0; i < len; i++) {
    shiftOut(PIN_DS_DIGITAL_FOUR, PIN_SHCP_DIGITAL_FOUR, LSBFIRST, data[i]);
  }
  // digitalWrite(PIN_STCP_DIGITAL_FOUR, HIGH);
}

// flip函数
void flip(int _index,
          String string1,
          String string2,
          String string3,
          String string4,
          String string5,
          int _duration)
{
  convertToRegisters(string1, string2, string3, string4, string5, qnums);

  //* 把qnum[4]打印出来 *//
  for (int i = 0; i < 4; i++) {
    Serial.print(qnums[i]);
    Serial.print(" "); // 或用 ',' / '\t' 等分隔符
  }
  Serial.println(); // 换行

  encodeShiftRegister(qnums, shiftData);
  writeRigister[_index - 1](shiftData, 4);
}

void convertToRegisters(String s1, String s2, String s3, String s4, String s5, String output[4])
{
  // s1 = L1 M1 R1
  // s2 = L2 M2 R2
  // s3 = L3 M3 R3
  // s4 = L4 M4 R4
  // s5 = L5 M5 R5

  output[0] = String(s4[2]) + String(s5[2]) + "00";
  output[1] = String(s5[1]) + String(s5[0]) + String(s4[0]) + "0";
  output[2] = String(s1[2]) + String(s2[2]) + String(s3[2]) + String(s3[1]);
  output[3] = String(s1[1]) + String(s1[0]) + String(s2[0]) + String(s3[0]);
}

/*   显示数字一和二的函数    */
void displayOnOneAndTwo(int num) {
  int tens, units;
  // 数字显示编码
  String digitsMap[11][5] = {
      {"111", "121", "121", "121", "111"}, // 0
      {"221", "221", "221", "221", "221"}, // 1
      {"111", "221", "111", "122", "111"}, // 2
      {"111", "221", "211", "221", "111"}, // 3
      {"121", "121", "111", "221", "221"}, // 4
      {"111", "122", "111", "221", "111"}, // 5
      {"111", "122", "111", "121", "111"}, // 6
      {"111", "221", "221", "221", "221"}, // 7
      {"111", "121", "111", "121", "111"}, // 8
      {"111", "121", "111", "221", "111"}, // 9
      {"222", "222", "222", "222", "222"}  // 10: invalid input
  };

  // 判断是否非法输入
  if (num < 0 || num > 99) {
    tens = 10;  // “无效”图案索引
    units = 10;
  } else {
    tens = num / 10;
    units = num % 10;
  }
  flip(DIGITAL_ONE,
       digitsMap[units][0],
       digitsMap[units][1],
       digitsMap[units][2],
       digitsMap[units][3],
       digitsMap[units][4],
       duration);
  flip(DIGITAL_TWO,
       digitsMap[tens][0],
       digitsMap[tens][1],
       digitsMap[tens][2],
       digitsMap[tens][3],
       digitsMap[tens][4],
       duration);

  digitalWrite(PIN_STCP_DIGITAL_ONE, HIGH);
  digitalWrite(PIN_STCP_DIGITAL_TWO, HIGH);
  delay(duration);
  // noFlip(interval);
}

/*   显示数字三和四的函数    */
void displayOnThreeAndFour(int num) {
  int tens, units;
  String digitsMap[11][5] = {
      {"111", "121", "121", "121", "111"}, // 0
      {"122", "122", "122", "122", "122"}, // 1
      {"111", "221", "111", "122", "111"}, // 2
      {"111", "122", "112", "122", "111"}, // 3
      {"122", "122", "111", "121", "121"}, // 4
      {"111", "122", "111", "221", "111"}, // 5
      {"111", "121", "111", "221", "111"}, // 6
      {"122", "122", "122", "122", "111"}, // 7
      {"111", "121", "111", "121", "111"}, // 8
      {"111", "122", "111", "121", "111"}, // 9
      {"222", "222", "222", "222", "222"}  // 10: invalid input
  };
  // 判断是否非法输入
  if (num < 0 || num > 99) {
    tens = 10;  // “无效”图案索引
    units = 10;
  } else {
    tens = num / 10;
    units = num % 10;
  }

  flip(DIGITAL_FOUR,
       digitsMap[tens][0],
       digitsMap[tens][1],
       digitsMap[tens][2],
       digitsMap[tens][3],
       digitsMap[tens][4],
       duration);
  flip(DIGITAL_THREE,
       digitsMap[units][0],
       digitsMap[units][1],
       digitsMap[units][2],
       digitsMap[units][3],
       digitsMap[units][4],
       duration);
  digitalWrite(PIN_STCP_DIGITAL_THREE, HIGH);
  digitalWrite(PIN_STCP_DIGITAL_FOUR, HIGH);
  delay(duration);
}
